package com.entfrm.auth.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.entfrm.auth.exception.ValidateCodeException;
import com.entfrm.auth.service.ValidateCodeService;
import com.entfrm.core.auth.entity.SysUser;
import com.entfrm.core.base.api.R;
import com.entfrm.core.base.constant.SqlConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 验证码接口实现
 *
 * @author entfrm
 * @create 2020-6-10
 */
@Slf4j
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * redis验证码目录
     **/
    private final String REDIS_DIR = "captcha:";

    /**
     * 宽
     */
    private final Integer WIDTH = 120;
    /**
     * 高
     */
    private final Integer HEIGHT = 40;
    /**
     * 编码长度
     */
    private final Integer CODE_COUNT = 4;
    /**
     * 干扰线数
     */
    private final Integer LINE_COUNT = 20;

    @Override
    public R genCode(String deviceId) {
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT, CODE_COUNT, LINE_COUNT);
        String code = lineCaptcha.getCode();
        redisTemplate.opsForValue().set(buildKey(deviceId), code, 60, TimeUnit.SECONDS);
        return R.ok(lineCaptcha.getImageBase64());
    }

    /**
     * 获取验证码
     *
     * @param deviceId 前端唯一标识/手机号
     */
    @Override
    public String getCode(String deviceId) {
        return (String) redisTemplate.opsForValue().get(buildKey(deviceId));
    }

    /**
     * 删除验证码
     *
     * @param deviceId 前端唯一标识/手机号
     */
    @Override
    public void remove(String deviceId) {
        redisTemplate.delete(buildKey(buildKey(deviceId)));
    }

    /**
     * 验证验证码
     */
    @Override
    public void validate(HttpServletRequest request) {
        String deviceId = request.getParameter("deviceId");
        if (StringUtils.isBlank(deviceId)) {
            throw new ValidateCodeException("请在请求参数中携带deviceId参数");
        }
        String code = this.getCode(deviceId);
        String codeInRequest;
        try {
            codeInRequest = ServletRequestUtils.getStringParameter(request, "validCode");
        } catch (ServletRequestBindingException e) {
            throw new ValidateCodeException("获取验证码的值失败");
        }
        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("请填写验证码");
        }

        if (code == null) {
            throw new ValidateCodeException("验证码不存在或已过期");
        }

        if (!StringUtils.equals(code, codeInRequest.toLowerCase())) {
            throw new ValidateCodeException("验证码不正确");
        }

        this.remove(deviceId);
    }

    private String buildKey(String deviceId) {
        return REDIS_DIR + SecureUtil.md5(deviceId);
    }

    @Override
    public R sendSmsCode(String phone) {
        Object tempCode = redisTemplate.opsForValue().get(buildKey(phone));
        if (tempCode != null) {
            log.error("用户:{}验证码未失效{}", phone, tempCode);
            return R.error("验证码未失效，请失效后再次申请");
        }

        List<SysUser> userList = jdbcTemplate.query(SqlConstants.QUERY_USER_PHONE, new Object[]{phone}, new BeanPropertyRowMapper(SysUser.class));
        if (userList == null || userList.size() == 0) {
            log.error("根据用户手机号{}查询用户为空", phone);
            return R.error("手机号不存在");
        }

        String code = RandomUtil.randomNumbers(4);
        log.info("短信发送请求消息中心 -> 手机号:{} -> 验证码：{}", phone, code);
        redisTemplate.opsForValue().set(buildKey(phone), code, 60, TimeUnit.SECONDS);
        return R.ok();
    }

    @Override
    public R sendEmailCode(String email) {
        //暂未实现
        return R.ok();
    }


}
